package com.smzd.ccms.business;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import com.smzd.ccms.dao.ChannelConfigDao;
import com.smzd.ccms.dao.CompletConfigDao;
import com.smzd.ccms.dao.CompletParamConfigDao;
import com.smzd.ccms.dao.SysDictDao;
import com.smzd.ccms.domain.Channel;
import com.smzd.ccms.domain.ChannelConfig;
import com.smzd.ccms.domain.CompletConfig;
import com.smzd.ccms.domain.CompletParamConfig;
import com.smzd.ccms.domain.SysDict;
import com.smzd.ccms.domain.Template;
import com.smzd.core.dao.IDao;
import com.smzd.framework.core.business.AbstractFGService;
import com.smzd.framework.utils.db.FGRowConverter;

@Service
public class ChannelConfigService extends AbstractFGService<ChannelConfig> {
	@Autowired
	private ChannelConfigDao dao;
	@Autowired
	private SysDictDao dictDao;
	@Autowired
	private CompletConfigDao ccDao;
	@Autowired
	private CompletParamConfigDao cpcDao;

	@Override
	protected IDao<ChannelConfig> getDao() {
		return dao;
	}

	@Override
    public FGRowConverter<ChannelConfig> getConverter() {
		return converter;
	}

	private static FGRowConverter<ChannelConfig> converter = new FGRowConverter<ChannelConfig>() {
		@Override
		public Object[] convert(ChannelConfig elem) {
			SysDict sysDict = elem.getSysDict();
			Object[] obj_sysDict;
			if (sysDict == null) {
				obj_sysDict = null;
			} else {
				Integer id_sysDict = sysDict.getId();
				String name_sysDict = sysDict.getName();
				obj_sysDict = new Object[] { id_sysDict, name_sysDict };
			}
			Template template = elem.getTemplate();
			Object[] obj_template;
			if (template == null) {
				obj_template = null;
			} else {
				Integer id_template = template.getId();
				String name_template = template.getName();
				obj_template = new Object[] { id_template, name_template };
			}
			Object[] fs = new Object[] { elem.getId(), elem.getName(), elem.getDescription(), obj_sysDict, obj_template };
			return fs;
		}
	};

	/**
	 * 根据给出的配置参数设置指定栏目的视角配置。<br>
	 * 只配置栏目视角，里面的小组件外面会处理。
	 * 
	 * @param channel
	 * @param dictids
	 * @param tmplids
	 * @param paths
	 * @return
	 * @throws Exception
	 */
	@Transactional
	public List<ChannelConfig> updateChannelConfigs(Channel channel, int[] dictids, int[] tmplids, String[] paths)
	        throws Exception {
		List<ChannelConfig> newChannelConfigs = new ArrayList<ChannelConfig>();
		// 根据给出的字典表id序列，查出所有的频道配置来。可能有，可能没有。因为频道的类型有可能变化了。
		List<ChannelConfig> cfgs = dao.getChannelConfigsByDictIds(channel.getId(), dictids);
		// 针对给出来的字典表序列挨个查找处理，没有的新添，有的更新。
		for (int i = 0; i < dictids.length; i++) {
			int dictid = dictids[i];// 字典表id
			int tmplid = tmplids[i];// 这个视角所配置的模板
			String path = paths[i];// 这个视角的名称
			ChannelConfig cfg = null;
			// 看看是否存在这个视角的栏目配置
			if (cfgs != null && cfgs.size() > 0) {
				for (ChannelConfig c : cfgs) {
					if (c.getSysDict().getId() - dictid == 0) {
						cfg = c; // 找到！
						break;
					}
				}
			}
			if (cfg == null) {// 添加新配置
				if (tmplid == 0) {
					throw new Exception("不能指定模板为空！");
				}
				cfg = new ChannelConfig();
				cfg.setSysDict(new SysDict(dictid));
				cfg.setScope(channel);
				cfg.setTemplate(new Template(tmplid));
				cfg.setName(channel.getId() + "-" + dictid + "-" + tmplid);
				cfg.setPath(path);
				dao.insert(cfg);
				newChannelConfigs.add(cfg);
			} else if (tmplid == 0) {// 删除已有配置？？这个功能也会使用吗？这个应该看看最后没有用的直接disabled掉就行了。
				cfg.setDisabled(true);
				dao.disableChannelConfig(cfg);
				throw new Exception("不能指定模板为空！");
			} else {// 修改已有配置
				cfg.setSysDict(new SysDict(dictid));
				cfg.setDisabled(false);
				cfg.getTemplate().setId(tmplid);
				cfg.setPath(path);
				dao.updateChannelConfig(cfg);
				newChannelConfigs.add(cfg);// 修改的也得列入进去，好继续配置
			}
		}
		if (cfgs != null && cfgs.size() > 0 && newChannelConfigs.size() > 0) {
			for (ChannelConfig c : cfgs) {
				boolean found = false;
				for (ChannelConfig cn : newChannelConfigs) {
					if (c.getSysDict().getId() - cn.getSysDict().getId() == 0) {
						found = true; // 找到！
						break;
					}
				}
				if (!found) {// 这个视角已经不存在了
					c.setDisabled(true);
					dao.disableChannelConfig(c);
				}
			}
		}
		return newChannelConfigs;
	}

	/**
	 * 把样板栏目的视角配置拷贝过来
	 * 
	 * @param target
	 * @param source
	 * @throws Exception
	 */
	public void duplicateChannelConfigs(Channel target, Channel source) throws Exception {
		// 获取模板栏目的配置
		int id = 100 + source.getType();
		SysDict dict = dictDao.getElemById(id);
		List<SysDict> dicts = dictDao.getSysDictsForChCfgs(source.getId(), dict.getValue());
		// 拿到模板的配置参数
		int size = dicts.size();
		int[] dictids = new int[size];// 定义视角的字典
		int[] tmplids = new int[size];// 视角的模板配置
		String[] paths = new String[size];// 目标栏目的名称
		List<ChannelConfig> tempchcfgs = new ArrayList<ChannelConfig>();
		for (int i = 0; i < size; i++) {
			SysDict sd = dicts.get(i);
			ChannelConfig chcfg = sd.getChannelConfig();
			chcfg.setSysDict(sd);
			dictids[i] = sd.getId();
			tmplids[i] = chcfg.getTemplate().getId();
			paths[i] = target.getName();
			tempchcfgs.add(chcfg);
		}
		// 直接交到模板视角配置处理器中，创建模板视角配置
		List<ChannelConfig> chcfgs = updateChannelConfigs(target, dictids, tmplids, paths);
		Assert.isTrue(chcfgs.size() == size);
		// 接下来需要逐个配置小组件了
		if (chcfgs.size() > 0) {
			for (int i = 0; i < size; i++) {
				// 下面这两个能匹配起来吗？是一对一的关系吗？
				ChannelConfig chcfg = chcfgs.get(i);
				ChannelConfig tempchcfg = tempchcfgs.get(i);
				Assert.isTrue(chcfg.getSysDict().getId() - tempchcfg.getSysDict().getId() == 0);
				duplicateCompletConfigs(chcfg, tempchcfg);
			}
		}
	}

	/**
	 * 把这个栏目视角的小组件配置拷贝过来
	 * 
	 * @param chcfg
	 * @param tempchcfg
	 */
	private void duplicateCompletConfigs(ChannelConfig chcfg, ChannelConfig tempchcfg) {
		// System.out.println("查询源栏目视角"+tempchcfg.getId()+"的组件配置情况：");
		List<CompletConfig> tempccs = ccDao.getCompletConfigsForChannelConfig(tempchcfg);
		// System.out.println("查询目标栏目视角"+chcfg.getId()+"的组件配置情况：");
		List<CompletConfig> ccs = ccDao.getCompletConfigsForChannelConfig(chcfg);
		List<CompletConfig> usedccs = new ArrayList<CompletConfig>();
		// 挨个处理模板的组件配置吧
		for (CompletConfig tempcc : tempccs) {
			// 这个直接插入，会造成已有的栏目视角的组件配置重复吧？除非是对新的栏目视角的复制。
			// 其实这个也不难，因为cc只有两个重要的属性，一个是spotid，另一个就是completid
			CompletConfig cc = null;
			if (ccs != null && ccs.size() > 0) {
				for (CompletConfig c : ccs) {
					if (c.getSpotid() - tempcc.getSpotid() == 0) {// 这是同一个位置上的
						cc = c;
						break;
					}
				}
			}
			// 如果没有找到，说明这是一个新的组件配置，直接插入
			int tempccid = tempcc.getId();
			if (cc == null) {
				tempcc.setId(null);
				tempcc.setScope(chcfg);
				// System.out.println("没找到组件配置，插入一个新的！");
				ccDao.insert(tempcc);
			} else {// 如果找到了，则更新组件配置
				if (cc.getComplet().getId() - tempcc.getComplet().getId() != 0) {// 变化了才更新，否则不更新
					cc.setComplet(tempcc.getComplet());
					// System.out.println("找到了，有变化，更新！");
					ccDao.updateComplet(cc);
					// }else{
					// System.out.println("找到了，没有变化，无需更新！");
				}
				usedccs.add(cc);
			}

			// 注意：
			// 由于cpConfigs属性由原先的List变成了Map，因此无法在一个方法中查出来（resultMap不支持Map类型的属性映射查询），需要两次查询，或者1+N次查询。
			// 当前方法（duplicateCompletConfigs）被两处调用，不知道cpConfigs属性是否已经初始化，因此在下面的处理过程中中，
			// 如果发现没有cpConfigs，则尝试去数据库查询获取。
			Map<String, CompletParamConfig> tempcpcMap = tempcc.getCpConfigs();// 这时模板的组件配置的参数
			Collection<CompletParamConfig> tempcpcs = null;
			if (tempcpcMap != null && tempcpcMap.size() > 0) {
				tempcpcs = tempcpcMap.values();
			}
			if (tempcpcs == null || tempcpcs.size() == 0) {// 如果模板中的组件配置没发现，则再查一次数据库
				// 这个地方的tempcc已经是新的了，因此必须保存之前的模板组件配置的ID
				// System.out.println("源组件配置的参数没调出来，查数据库！");
				tempcpcs = cpcDao.getParamConfigsForCompletConfig(tempccid);// (tempcc.getId());
			}

			if (cc == null) {// 如果当前栏目视角没有该位置的组件配置，那么是直接复制了模板里面的组件配置，因此参数配置直接拿过来就行了。
				// 拿到模板组件配置的参数
				if (tempcpcs != null && tempcpcs.size() > 0) {
					for (CompletParamConfig cpc : tempcpcs) {
						cpc.setId(null);
						cpc.setScope(tempcc);// 这时是新的了，没问题
						// System.out.println("目标组件配置是新建立的，肯定没有参数，插入好吧！");
						cpcDao.insert(cpc);
					}
					// }else{
					// System.out.println("源组件配置也不存在参数配置，所以不用设置了");
				}
			} else {
				// 这个时候比较麻烦了，需要挨个核对
				Map<String, CompletParamConfig> cpcMap = cc.getCpConfigs();
				Collection<CompletParamConfig> cpcs = null;
				if (cpcMap != null && cpcMap.size() > 0) {
					cpcs = cpcMap.values();
				}
				if (cpcs == null || cpcs.size() == 0) {// 如果目标组件配置没发现参数，则再查一次数据库
					// 这个地方的tempcc已经是新的了，因此必须保存之前的模板组件配置的ID
					cpcs = cpcDao.getParamConfigsForCompletConfig(cc.getId());// (tempcc.getId());
				}
				if (cpcs == null || cpcs.size() == 0) {
					if (tempcpcs != null && tempcpcs.size() > 0) {
						for (CompletParamConfig cpc : tempcpcs) {
							cpc.setId(null);
							cpc.setScope(tempcc);// 这时是新的了，没问题
							cpcDao.insert(cpc);
						}
					}
				} else {// 这下得挨个比较了
					List<CompletParamConfig> used = new ArrayList<CompletParamConfig>();
					for (CompletParamConfig tempcpc : tempcpcs) {
						boolean found = false;
						for (CompletParamConfig cpc : cpcs) {
							if (tempcpc.getKey().equals(cpc.getKey())) {// 这是同一个，直接更新就好了
								tempcpc.setId(cpc.getId());
								// tempcpc.setScope(cc);//这个不更新，所以无所谓
								cpcDao.updateValue(tempcpc);// 这个地方并不更新scopeid
								used.add(cpc);
								found = true;
								break;
							}
						}
						if (!found) {// 没有，则需要插入一个
							CompletParamConfig cpc = new CompletParamConfig(tempcpc);
							cpc.setScope(cc);
							cpcDao.insert(cpc);
						}
					}
				}
			}
		}
	}

}